/*************************************************************************
 *                                                                       *
 * This file is part of the FreeMAPs distribution.  For full copyright   *
 * information, see COPYRIGHT and COPYING.LESSER.                        *
 *                                                                       *
 * Author:           sqbang                                              *
 * Email:            sqbang@ustc.edu                                     *
 * Date:             2011.03.30                                          *
 *                                                                       *
 *************************************************************************/

#ifndef FreeMAPs_include_COMMON_base_Common_h
#define FreeMAPs_include_COMMON_base_Common_h

#include "FreeMAPsConfig.h"

//C++headers
#include <string>
#include <set>
#include <sstream>
#include <cmath>

//COMMON headers
#include "COMMON/base/DefaultParameters.h"

#ifdef FREEMAPS_WIN32
const std::string RTN_FLAG = "\t\n";
#else
const std::string RTN_FLAG = "\n";
#endif

#undef DISALLOW_COPY_AND_ASSIGN
#define DISALLOW_COPY_AND_ASSIGN(ClassName)           \
    ClassName (const ClassName&);                     \
    void operator = (const ClassName&)

/**
 * Throw an error assertion from within any C++ source code.  The
 * macro argument may be any standard ostream expression.  The file and
 * line number of the abort are also printed.
 */
#undef FREEMAPS_ERROR
#define FREEMAPS_ERROR(X)                                              \
    do                                                                 \
    {                                                                  \
        std::ostringstream tboxos;                                     \
        tboxos << X << std::ends;                                      \
        FreeMAPs::Utilities::abort(tboxos.str(), __FILE__, __LINE__);  \
    } while (0)

#undef FREEMAPS_FIND_BUG
#define FREEMAPS_FIND_BUG()                                            \
    do                                                                 \
    {                                                                  \
        std::ostringstream tboxos;                                     \
        tboxos << "if reach here, it is a bug." << std::ends;          \
        FreeMAPs::Utilities::abort(tboxos.str(), __FILE__, __LINE__);  \
    } while (0)

#undef FREEMAPS_NOT_FINISHED
#define FREEMAPS_NOT_FINISHED()                                            \
    do                                                                     \
    {                                                                      \
        std::ostringstream tboxos;                                         \
        tboxos << "this function will be finished in future." << std::ends;\
        FreeMAPs::Utilities::abort(tboxos.str(), __FILE__, __LINE__);      \
    } while (0)

/**
 * Print a warning without exit.  Print file and line number of the warning.
 */
#undef FREEMAPS_WARNING
#define FREEMAPS_WARNING(X)                                  \
    do {                                                     \
        std::ostringstream tboxos;                           \
        tboxos << X << std::ends;                            \
        FreeMAPs::Logger::get_instance()->log_warning(         \
                tboxos.str(), __FILE__, __LINE__);           \
    } while (0)

/**
 * Print a debug without exit.  Print file and line number of the debug.
 */
#undef FREEMAPS_DEBUG
#define FREEMAPS_DEBUG(X)                                    \
    do                                                       \
    {                                                        \
        std::ostringstream tboxos;                           \
        tboxos << X << std::ends;                            \
        FreeMAPs::Logger::get_instance()->log_debug(           \
                tboxos.str(), __FILE__, __LINE__);           \
    } while (0)

/**
 * Throw an error assertion from within any C++ source code if the
 * given expression is not true.  This is a parallel-friendly version
 * of assert.
 * The file and line number of the abort are also printed.
 */
/*
 * check assert
 */
#undef FREEMAPS_ASSERT
#ifdef FREEMAPS_DEBUG_CHECK_ASSERTIONS

#define FREEMAPS_ASSERT(EXP)                                               \
    do                                                                     \
    {                                                                      \
        if (!(EXP))                                                        \
        {                                                                  \
            std::ostringstream tboxos;                                     \
            tboxos << "Failed assertion: " << # EXP << std::ends;          \
            FreeMAPs::Utilities::abort(tboxos.str(), __FILE__, __LINE__);  \
        }                                                                  \
    } while (0)
#else

/**
* No assertion checking
*/
#define FREEMAPS_ASSERT(EXP) ((void)(EXP))

#endif
/*
 * end check assert
 */

/**
 * Throw an error assertion from within any C++ source code if the
 * given expression is not true.  This is a parallel-friendly version
 * of assert.
 * The file and line number of the abort are also printed along with the
 * supplied message.
 */
#undef FREEMAPS_ASSERT_MSG
#ifdef FREEMAPS_DEBUG_CHECK_ASSERTIONS

#define FREEMAPS_ASSERT_MSG(EXP, MSG)                                       \
    do                                                                      \
    {                                                                       \
        if (!(EXP))                                                         \
        {                                                                   \
            std::ostringstream tboxos;                                      \
            tboxos << "Failed assertion: " << # EXP << std::endl;           \
            tboxos << MSG << std::ends;                                     \
            FreeMAPs::Utilities::abort(tboxos.str(), __FILE__, __LINE__);   \
        }                                                                   \
    } while (0)
#else

/**
* No assertion checking
*/
#define FREEMAPS_ASSERT_MSG(EXP, MSG)

#endif //define FREEMAPS_DEBUG_CHECK_ASSERTIONS

/*
 * check LT
 */
#undef FREEMAPS_ASSERT_LT
#ifdef FREEMAPS_DEBUG_CHECK_ASSERTIONS

#define FREEMAPS_ASSERT_LT(EXP1, EXP2)                                              \
    do                                                                              \
    {                                                                               \
        if (!(static_cast<double>(EXP1) < static_cast<double>(EXP2)))               \
        {                                                                           \
            std::ostringstream tboxos;                                              \
            tboxos << "Failed assertion: " << #EXP1 << " < " << #EXP2 << std::ends; \
            FreeMAPs::Utilities::abort(tboxos.str(), __FILE__, __LINE__);           \
        }                                                                           \
    } while (0)
#else

#define FREEMAPS_ASSERT_LT(EXP1, EXP2) (void)(EXP1);(void)(EXP2)

#endif
/*
 * end check LT
 */

/*
 * check LE
 */
#undef FREEMAPS_ASSERT_LE
#ifdef FREEMAPS_DEBUG_CHECK_ASSERTIONS

#define FREEMAPS_ASSERT_LE(EXP1, EXP2)                                               \
    do                                                                               \
    {                                                                                \
        if (!(static_cast<double>(EXP1) <= static_cast<double>(EXP2)))               \
        {                                                                            \
            std::ostringstream tboxos;                                               \
            tboxos << "Failed assertion: " << #EXP1 << " <= " << #EXP2 << std::ends; \
            FreeMAPs::Utilities::abort(tboxos.str(), __FILE__, __LINE__);            \
        }                                                                            \
    } while (0)
#else

#define FREEMAPS_ASSERT_LE(EXP1, EXP2) (void)(EXP1);(void)(EXP2)

#endif
/*
 * end check LE
 */

/*
* check GT
*/
#undef FREEMAPS_ASSERT_GT
#ifdef FREEMAPS_DEBUG_CHECK_ASSERTIONS

#define FREEMAPS_ASSERT_GT(EXP1, EXP2)                                              \
    do                                                                              \
    {                                                                               \
        if (!(static_cast<double>(EXP1) > static_cast<double>(EXP2)))               \
        {                                                                           \
            std::ostringstream tboxos;                                              \
            tboxos << "Failed assertion: " << #EXP1 << " > " << #EXP2 << std::ends; \
            FreeMAPs::Utilities::abort(tboxos.str(), __FILE__, __LINE__);           \
        }                                                                           \
    } while (0)
#else

#define FREEMAPS_ASSERT_GT(EXP1, EXP2) (void)(EXP1);(void)(EXP2)

#endif
/*
 * end check GT
 */

/*
 * check GE
 */
#undef FREEMAPS_ASSERT_GE
#ifdef FREEMAPS_DEBUG_CHECK_ASSERTIONS

#define FREEMAPS_ASSERT_GE(EXP1, EXP2)                                               \
    do                                                                               \
    {                                                                                \
        if (!(static_cast<double>(EXP1) >= static_cast<double>(EXP2)))               \
        {                                                                            \
            std::ostringstream tboxos;                                               \
            tboxos << "Failed assertion: " << #EXP1 << " >= " << #EXP2 << std::ends; \
            FreeMAPs::Utilities::abort(tboxos.str(), __FILE__, __LINE__);            \
        }                                                                            \
    } while (0)
#else

#define FREEMAPS_ASSERT_GE(EXP1, EXP2) (void)(EXP1); (void)(EXP2)

#endif
/*
 * end check GE
 */

/*
* check EQ
*/
#undef FREEMAPS_ASSERT_EQ
#ifdef FREEMAPS_DEBUG_CHECK_ASSERTIONS

#define FREEMAPS_ASSERT_EQ(EXP1, EXP2)                                                           \
    do                                                                                           \
    {                                                                                            \
        if (std::abs (static_cast<double>(EXP1) - static_cast<double>(EXP2)) > FREEMAPS_TOL())   \
        {                                                                                        \
            std::ostringstream tboxos;                                                           \
            tboxos << "Failed assertion: " << #EXP1 << " == " << #EXP2 << std::ends;             \
            FreeMAPs::Utilities::abort(tboxos.str(), __FILE__, __LINE__);                        \
        }                                                                                        \
    } while (0)
#else

#define FREEMAPS_ASSERT_EQ(EXP1, EXP2) (void)(EXP1);(void)(EXP2)

#endif
/*
 * end check EQ
 */

/*
 * check NE
 */
#undef FREEMAPS_ASSERT_NE
#ifdef FREEMAPS_DEBUG_CHECK_ASSERTIONS

#define FREEMAPS_ASSERT_NE(EXP1, EXP2)                                                           \
    do                                                                                           \
    {                                                                                            \
        if (std::abs (static_cast<double>(EXP1) - static_cast<double>(EXP2)) < FREEMAPS_TOL())   \
        {                                                                                        \
            std::ostringstream tboxos;                                                           \
            tboxos << "Failed assertion: " << #EXP1 << " != " << #EXP2 << std::ends;             \
            FreeMAPs::Utilities::abort(tboxos.str(), __FILE__, __LINE__);                        \
        }                                                                                        \
    } while (0)
#else

#define FREEMAPS_ASSERT_NE(EXP1, EXP2) (void)(EXP1);(void)(EXP2)

#endif
/*
 * end check NE
 */

/*
* check dimension indice
*/
#undef FREEMAPS_ASSERT_DIM_INDICE
#ifdef FREEMAPS_DEBUG_CHECK_ASSERTIONS

#define FREEMAPS_ASSERT_DIM_INDICE(d)                                               \
    FREEMAPS_ASSERT(d >= 0 && d < FREEMAPS_DIM)
#else

#define FREEMAPS_ASSERT_DIM_INDICE(d) (void)(d)

#endif
/*
 * end check NE
 */

/*
 * check valid point
 */
#undef FREEMAPS_ASSERT_VALID_POINTER
#ifdef FREEMAPS_DEBUG_CHECK_ASSERTIONS

#define FREEMAPS_ASSERT_VALID_POINTER(P)                                          \
    do                                                                            \
    {                                                                             \
        if (!P)                                                                   \
        {                                                                         \
            std::ostringstream tboxos;                                            \
            tboxos << "Failed assertion: invalid pointer of " << #P << std::ends; \
            FreeMAPs::Utilities::abort(tboxos.str(), __FILE__, __LINE__);         \
        }                                                                         \
    } while (0)
#else

#define FREEMAPS_ASSERT_VALID_POINTER(P) ((void)(P))

#endif
/*
 * end check NE
 */

/*
 * return true if it is not a NULL pointer
 */
#undef FREEMAPS_IS_NULL_POINTER
#define FREEMAPS_IS_NULL_POINTER(P) ((!(P)) ? true : false)

/*
 * The libmesh_dbg_var() macro indicates that an argument to a function
 * is used only in debug mode (i.e., when NDEBUG is not defined).
 * used to avoid warning of no use variable.
 */
#undef FREEMAPS_DBG_VAR
#ifdef FREEMAPS_DEBUG
#define FREEMAPS_DBG_VAR(var) var
#else
#define FREEMAPS_DBG_VAR(var)
#endif

#undef FREEMAPS_HERE
#define FREEMAPS_HERE()      \
    {                        \
        std::cout << "[" << FREEMAPS_MPI::get_MPI_world().get_rank() << "] " << __FILE__ << ", line " << __LINE__ << std::endl; \
    }

#undef FREEMAPS_SHOW_DEBUG
#define FREEMAPS_SHOW_DEBUG(X)            \
    if (FREEMAPS_DEBUG_SHOW_MESSAGE)      \
{                                         \
    std::ostringstream os;                \
    os << X;                              \
    std::cout << os.str() << std::endl;   \
}

namespace FreeMAPs
{

/**
 * @TODO to be added in future
 */
void
FREEMAPS_CHKERR(
    const int ierr);

/**
 * do not move the following functions to other place,
 * because the following function will be VERY base that it can NOT
 * include other head files.
 */
namespace Utilities
{

/**
  * Aborts the run after printing an error message with file and
  * linenumber information.
  */
int
abort(
    const std::string& message,
    const std::string& file_name,
    const int line);

} //namespace Utilities

} //namespace FreeMAPs

#include "COMMON/base/Common.inl"

#endif
